package com.hanxiaozhang.http;

import com.hanxiaozhang.http.config.Context;
import com.hanxiaozhang.http.dispatcher.HandlerMapper;
import com.hanxiaozhang.http.dispatcher.HttpRequestDispatcher;
import com.hanxiaozhang.http.netty.HttpRequestHandler;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.handler.timeout.IdleStateHandler;

import java.net.InetSocketAddress;
import java.util.concurrent.TimeUnit;

/**
 * 功能描述: <br>
 * 〈基础Http服务〉
 *
 * @Author:hanxiaozhang
 * @Date: 2022/1/7
 */
public class BaseHttpServer{

    private String hostIp = "127.0.0.1";

    private int port = 80;

    private HttpRequestHandler handler;

    private EventLoopGroup workGroup;

    private EventLoopGroup masterGroup;

    private ServerBootstrap serverBootstrap;

    public BaseHttpServer(HttpRequestHandler handler) {
        this.handler = handler;
    }

    public void setConfig(Context context) {

        this.hostIp = context.getString("hostIp", "0.0.0.0");
        this.port = context.getInt("port", 80);

        HttpRequestDispatcher dispatcher = new HttpRequestDispatcher();
        dispatcher.setMapper((HandlerMapper) context.getObject("handlerMapper"));
        handler.setDispatcher(dispatcher);
    }


    public void start() {
        workGroup = new NioEventLoopGroup();
        masterGroup = new NioEventLoopGroup();
        serverBootstrap = new ServerBootstrap();
        try {
            serverBootstrap.group(masterGroup, workGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 10240)
                    .option(ChannelOption.TCP_NODELAY, true)
                    .option(ChannelOption.SO_REUSEADDR, true)
                    .option(ChannelOption.SO_RCVBUF, 1024 * 1024)
                    .option(ChannelOption.SO_SNDBUF, 1024 * 1024)
                    .childHandler(new ChannelInitializer<SocketChannel>() {

                        @Override
                        protected void initChannel(SocketChannel channel) {
                            channel.pipeline().addLast("encoder", new HttpResponseEncoder());
                            channel.pipeline().addLast("decoder", new HttpRequestDecoder());
                            channel.pipeline().addLast("aggregator", new HttpObjectAggregator(1024 * 1024));
                            channel.pipeline().addLast("chunkHandler", new ChunkedWriteHandler());
                            channel.pipeline().addLast("idleHandler", new IdleStateHandler(5, 5, 5, TimeUnit.SECONDS));
                            channel.pipeline().addLast("mainHandler", handler);
                        }
                    });

            Channel channel = serverBootstrap.bind(new InetSocketAddress(hostIp, port)).sync().channel();
            channel.closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            masterGroup.shutdownGracefully();
            workGroup.shutdownGracefully();
        }
    }

}